package org.lestr.secuel.noyau;

import java.util.ArrayList;
import java.util.Collection;

import org.lestr.secuel.m3ds.Affichage;


public class BoucleDeJeu extends Thread {

	
	// Variables globales
	private static BoucleDeJeu boucle;
	
	
	// Déclaration des attributs
	private Collection<BoucleDeJeuEcouteur> écouteurs;
	private Affichage affichage;
	private boolean stop;
	private boolean ordreStop;
	private boolean pause;
	private boolean ordrePause;
	
	
	/** Retourne le singleton */
	public static synchronized BoucleDeJeu retBoucleDeJeu(){
		
		// Instancie si pas déjà fait
		if(boucle == null)
			boucle = new BoucleDeJeu();
		
		// Retourne le singleton
		return boucle;
		
	}// FIN retBoucleDeJeu 
	
	
	/** Constructeur interdit */
	private BoucleDeJeu(){
		
		// Instanciation des attributs
		écouteurs = new ArrayList<BoucleDeJeuEcouteur>();
		affichage = null;
		stop = true;
		ordreStop = true;
		pause = false;
		ordrePause = false;
		
		// Démarre la boucle
		start();
		
	}// FIN Constructeur
	
	
	/** Ajoute un écouteur */
	public synchronized void ajoutEcouteur(BoucleDeJeuEcouteur écouteur){
	
		// Pause
		pause(true);
		
		écouteurs.add(écouteur);
		
		// Sort de pause
		pause(false);
		
	}// FIN ajoutEcouteur
	
	
	/** Retire un écouteur */
	public synchronized void retraitEcouteur(BoucleDeJeuEcouteur écouteur){
	
		// Pause
		pause(true);
		
		// Retire
		écouteurs.remove(écouteur);
		
		// Sort de pause
		pause(false);
		
	}// FIN retraitEcouteur
	
	
	/** Défini l'affichage 3D à boucler */
	public synchronized void defAffichage(Affichage affichage){
	
		// Arrête
		arrêter();
		
		// Mémorise
		this.affichage = affichage;
		
		// Démarre
		démarrer();
		
	}// FIN defAffichage
	
	
	/** Démarre la boucle */
	public synchronized void démarrer() {
		
		// Si déjà démarré sort
		if(!stop)
			return;
		
		// Donne l'ordre de continuer
		ordreStop = false;
		
		// Réveille la boucle
		notifyAll();
		
		// S'arrête tant que ce n'est pas fait
		while(stop)
			try { wait(); } catch (InterruptedException e) {}
		
	}// FIN arrêter
	
	
	/** Arrête la boucle */
	public synchronized void arrêter() {
		
		// Si déjà arrêté sort
		if(stop)
			return;
		
		// Donne l'ordre d'arrêter
		ordreStop = true;
		
		// S'arrête tant que ce n'est pas fait
		while(!stop)
			try { wait(); } catch (InterruptedException e) {}
		
	}// FIN arrêter

	
	/** Un écouteur permet de continuer après avoir arrêté la boucle */
	public void continuer(){
		
		notifyAll();
		
	}// FIN continuer

	
	@Override
	public void run() {
	
		// Blindage
		if(affichage == null)
			return;
		
		// Attends l'ordre de démarré
		while(ordreStop)
			try {
				synchronized (this) { wait(); }
			} catch (InterruptedException e) {}
		
		// Boucle infinie
		while(true){
		
			// Est démarré
			stop = false;
			
			// Réveille ceux qui attendent
			synchronized (this) { notifyAll(); }
			
			// Boucle de jeu
			while(!ordreStop){
			
				// Demande aux écouteurs si l'on peut continuer
				while(!peutContinuer())
					try {
						synchronized (this) { wait(); }
					} catch (InterruptedException e) {}
				
				// Actualise l'affichage
				affichage.actualiser();
				
			}
			
			// Est arrêté
			stop = true;
			
			// Réveille ceux qui attendent
			synchronized (this) { notifyAll(); }
			
			// Attends l'ordre de démarrer
			while(ordreStop)
				try {
					synchronized (this) { wait(); }
				} catch (InterruptedException e) {}
		
		}
		
	}// FIN run
	
	
	/** Met/sort de pause */
	private void pause(boolean ordre){
		
		// Si s'est un ordre de pause
		if(ordre){
			
			// Si on y est déjà, sort
			if(pause)
				return;
			
			// Donne l'ordre
			ordrePause = true;
		
			// Attends que ce soit fait
			while(!pause)
				try { wait(); } catch (InterruptedException e) {}
			
		}
		
		// Si c'est un ordre de reprise
		else{
			
			// Si c'est déjà fait, sort
			if(!pause)
				return;
			
			// Donne l'ordre
			ordrePause = false;
			
			// Réveille la boucle
			notifyAll();
		
			// Attends que ce soit fait
			while(pause)
				try { wait(); } catch (InterruptedException e) {}
			
		}
		
	}// FIN pause
	
	

	/** Retourne vrai si l'on peut continuer */
	private synchronized boolean peutContinuer(){
	
		// S'il y a un ordre de pause
		if(ordrePause){
			
			// On est en pause
			pause = true;
		
			// Réveille celui qui a donné l'ordre
			notifyAll();
			
			// Tant que l'on est en pause, s'arrête
			while(ordrePause)
				try { wait(); } catch (InterruptedException e) {}
			
			// On n'est plus en pause
			pause = false;
		
			// Réveille celui qui a arrêté la pause
			notifyAll();
			
		}
		
		// Demande aux écouteurs s'il n'y a pas d'objection
		for(BoucleDeJeuEcouteur écouteur : écouteurs)
			if(!écouteur.peutContinuer())
				return false;
		
		// Si non, retourne vrai		
		return true;
		
	}// FIN peutContinuer
	
	
}// FIN Classe
